
export class EventEmitter {
  
  private _events: Map<string | symbol, Function[]>; // 键表示事件名，对应的值为相应的监听器数组
  private _maxListeners: number;

  constructor() {
    this._events = new Map();
    this._maxListeners = EventEmitter.defaultMaxListeners;
  }

  /**
   * 事件 'newListener' 的内部实现方法
   * - 在将侦听器添加到其内部侦听器数组之前，EventEmitter实例将发出自己的“newListener”事件。
   * @param event 正在侦听的事件的名称
   * @param listener 事件处理函数
   * @example
   * ```ts
   * import { EventEmitter } from '@jcstdio/jc-utils'
   * 
   * class MyEmitter extends EventEmitter {}
   * 
   * const myEmitter = new MyEmitter();
   * // 只做一次，这样我们就不会永远循环
   * myEmitter.once('newListener', (event: string, listener: any) => {
   *   if (event === 'myEvent') {
   *     myEmitter.on('myEvent', () => {
   *       console.log('B');
   *     });
   *   }
   * });
   * 
   * 
   * myEmitter.on('myEvent', (...params:any[]) => {
   *   console.log('A');
   * });
   * 
   * // 触发 event 事件
   * myEmitter.emit('myEvent','other params');
   * // Prints:
   * //   B
   * //   A
   * ```
   */
  private _pushListener(event: string | symbol, listener: (...args: any[]) => void){
    // 当添加新的监听器到监听器数组之前，将自己发出 `newListener` 事件
    this.emit('newListener',event, listener); 
    this._events.get(event)?.push(listener);
  }

  /**
   * 将 `listener` 函数添加到名为 `event` 的事件的 listeners 数组的末尾。
   * - 不检查是否已经添加了 `listener`。
   * - 多次调用传递 `event` 和  `listener` 的相同组合将导致多次添加和调用 `listener` 。
   * @param event 时间名
   * @param listener 订阅该事件的监听器函数
   * @returns 返回对 `EventEmitter`的引用，以便可以链接调用。
   * @since v0.0.19 重大变化：当添加新的监听器到监听器数组时，将自己发出 `newListener` 事件
   */
  public addListener(event: string | symbol, listener: (...args: any[]) => void): this {
    if (!this._events.has(event)) {
      this._events.set(event, []);
    }
    // this._events.get(event)?.push(listener);
    this._pushListener(event, listener)
    // 已达最大监听器数量警告
    if (this._events.get(event)?.length === this._maxListeners) {
      console.warn(`[EventEmitter Warn] Max listeners (${this._maxListeners}) reached for event ${String(event)}`);
    }
    return this
  }

  /**
   * 添加 listener, addListener 的同名方法
   * @param event 事件名
   * @param listener 订阅该事件的监听器函数
   */
  public on(event: string | symbol, listener: (...args: any[]) => void): this {
    return this.addListener(event, listener)
  }

  /**
   * 为名为 event 的事件添加 一次性 的 listener 函数。
   * 下次 event 被触发时，该 listener 被移除，然后被调用。
   * @param event 事件名字符串
   * @param listener 事件回调函数
   * @returns 返回对 `EventEmitter`的引用，以便可以链接调用。
   */
  once(event: string | symbol, listener: Function) {
    // 包装为一个自带移除功能的 监听器（订阅者），以事件一次性监听（订阅）
    const onceListener = (...args: any[]) => {
      this.removeListener(event, onceListener); // 移除该一次性监听器
      listener(...args);                        // 执行监听器函数
    };
    this.addListener(event, onceListener);
    return this;
  }



  /**
   * Event: 'removeListener'
   * - 删除侦听器后，将发出“removeListener”事件。
   * @param eventName 
   * @param listener 
   * @since v0.0.19
   */
  private _rmListener(eventName: string | symbol, listener:Function) {
    this.emit('removeListener')
  }

  /**
   * 从名为 event 的事件的侦听器数组中移除指定的 listener。
   * @param event 事件名
   * @param listener 事件监听器函数（事件的订阅者函数）
   * @returns 返回对 `EventEmitter`的引用，以便可以链接调用。
   */
  removeListener(event: string | symbol, listener: (...args: any[]) => void): this {
    if (!this._events.has(event)) {
      return this;
    }
    const listeners = this._events.get(event);
    const index = listeners?.indexOf(listener);
    if (index !== undefined && index !== -1) {
      listeners?.splice(index, 1);
    }
    this._rmListener(event,listener)
    return this
  }

  /**
   * 从名为 event 的事件的侦听器数组中移除指定的 listener。 removeListener 的同名方法
   * @param event 
   * @param listener 
   */
  off(event: string | symbol, listener: (...args: any[]) => void): this {
    return this.removeListener(event, listener)
  }

  /**
   * 移除所有的 Listener
   * @param event 事件名
   * @returns 返回对 `EventEmitter`的引用，以便可以链接调用。
   */
  public removeAllListeners(event?: string | symbol): this {
    if (event) {
      this._events.delete(event);
    } else {
      this._events.clear();
    }
    return this
  }

  /**
   * 获取所有事件名
   * @returns 事件名数组
   * @since v0.0.19 新增该方法
   */
  public eventNames(): Array<string | symbol> {
    return [...this._events.keys()]
  }

  /**
   * 设定最大 Listener 数量
   * @param n 最大 Listener 数量
   */
  public set maxListeners(n: number) {
    this._maxListeners = n;
  }

  /**
   * 获取最大 Listener 数量
   * - 默认情况下，任何单个事件最多可以注册10个侦听器。可以使用emitter.setMaxListeners(n)方法为单个EventEmitter实例更改此限制。
   */
  public get maxListeners(): number {
    return this._maxListeners;
  }

  /**
   * 设定最大 Listener 数量
   * - setter maxListeners 的方法形式
   * @param n 最大 Listener 数量
   * @return 返回对 `EventEmitter`的引用，以便可以链接调用。
   */
  public setMaxListeners(n: number): this {
    this._maxListeners = n;
    return this
  }


  /**
   * 获取最大 Listener 数量
   * - 默认情况下，任何单个事件最多可以注册10个侦听器。可以使用emitter.setMaxListeners(n)方法为单个EventEmitter实例更改此限制。
   * - getter maxListeners 的方法形式
   */
  public getMaxListeners(): number {
    return this._maxListeners;
  }

  /**
   * 返回名为 `event` 的事件的侦听器数组的副本。
   * 
   * @param event 一个表示事件（key）的字符串
   * @returns 事件回调
   */
  public listeners(event: string | symbol): Function[] {
    let _: Function[] | undefined = this._events.get(event);
    if (_) {
      return [..._]
    }
    return [];
  }

  /**
   * 返回名为 `eventName` 的事件的侦听器数组的副本，包括任何包装(如由 `.once()` 创建的包装)。
   * @param event 
   * @returns 
   */
  public rawListeners(event: string | symbol) {
    return this._events.get(event) || [];
  }

  /**
   * 事件发生器
   * 
   * - 若指定事件不存在，则返回 flase
   * - 反之，则执行指定事件的所有回调，并返回true
   * 
   * @param event 事件名字符串（key）
   * @param args 事件回调的参数
   * @returns 表示事件是否存在的布尔值
   */
  public emit(event: string | symbol, ...args: any[]): boolean {
    // 索引对应于给定事件的监听器数组
    const listeners = this._events.get(event);
    // 若事件不存在直接返回 false
    if (!listeners) {
      return false;
    }
    // 执行给定事件的所有监听器函数（回调）
    listeners.forEach((listener) => {
      listener(...args);
    });
    return true;
  }

  /**
   * 返回侦听名为eventName的事件的侦听器数量。如果提供了listener，它将返回在事件的侦听器列表中找到该侦听器的次数。
   * 
   * 当指定的事件不存在时返回值为0
   * @param event 监听的事件名
   * @param listener 事件处理函数
   * @returns 指定事件的现有监听器的数量
   * @since v0.0.19 允许指定特定的监听器函数（listener）进行统计
   * ```ts
   * import { EventEmitter } from '@jcstdio/jc-utils'
   * 
   * const myEmitter = new EventEmitter();
   * myEmitter.on('event', () => {});
   * myEmitter.on('event', () => {});
   * console.log(myEmitter.listenerCount('event'));
   * // Prints: 2
   * ```
   */
  public listenerCount(event: string | symbol, listener?:Function): number {
    // const listeners: Function[] | undefined = this._events.get(event);
    // return listeners?.length || 0;
    const listeners = this._events.get(event);
    // 无事件，也返回 0
    if (!listeners) {
        return 0;
    }
    // 未指定监听器，返回该事件所有监听器的数量
    if (!listener) {
        return listeners.length;
    }
    // 否则（指定的监听器），遍历该事件的监听器数组，从0开始统计，有一个算一个
    let count = 0;
    for (let i = 0; i < listeners.length; i++) {
      if (listeners[i] === listener) {
        count++;
      }
    }
    return count;
  }

  /**
   * 将 `listener` 函数添加到名为 `event` 的事件的 listeners 数组的开头。
   * - 不检查是否已经添加了 `listener`。
   * - 多次调用传递“事件名称”和 `listener` 的相同组合将导致多次添加和调用 `listener`。
   * @param event 事件名
   * @param listener 事件回调函数
   * @returns 返回对 `EventEmitter`的引用，以便可以链接调用。
   */
  prependListener(event: string | symbol, listener: Function): this {
    if (!this._events.has(event)) {
      this._events.set(event, []);
    }
    this._events.get(event)?.unshift(listener);
    if (this._events.get(event)?.length === this._maxListeners) {
      console.warn(`Max listeners (${this._maxListeners}) reached for event ${String(event)}`);
    }
    return this;
  }

  /**
   * 将名为 `event` 的事件的 **一次性** 的 `listener` 函数添加到 listeners 数组的`_beginning_ ` 中。
   * 下次 `event` 事件被触发时，该 监听器 被移除，然后被调用。
   * @param event 
   * @param listener 监听器回调函数
   */
  prependOnceListener(event: string | symbol, listener: Function) {
    const onceListener = (...args: any[]) => {
      this.removeListener(event, onceListener);
      listener(...args);
    };
    this.prependListener(event, onceListener);
    return this
  }

  
  /**
   * 此符号应用于安装一个监听器，仅用于监控`'error'`事件。在调用常规的`'error'`侦听器之前，调用使用此符号安装的侦听器。
   *
   * 使用此符号安装侦听器不会改变发出 `'error'` 事件后的行为，因此，如果没有安装常规的 `'error'` 侦听器，进程仍会崩溃。
   */
  static readonly errorMonitor: unique symbol;
  static readonly captureRejectionSymbol: unique symbol;
  /**
   * 设置或获取所有发射器的默认 captureRejection 值。
   */
  static captureRejections: boolean;
  /**
   * @since v0.0.19 新增 defaultMaxListeners，表示默认的最大监听器数量
   */
  static defaultMaxListeners: number=10;

}









